package top.lingkang.finalsql.sql;

import top.lingkang.finalsql.annotation.Id;
import top.lingkang.finalsql.config.SqlConfig;
import top.lingkang.finalsql.constants.IdType;
import top.lingkang.finalsql.dialect.SqlDialect;
import top.lingkang.finalsql.error.FinalException;
import top.lingkang.finalsql.error.FinalSqlException;
import top.lingkang.finalsql.utils.ClassUtils;
import top.lingkang.finalsql.utils.NameUtils;
import top.lingkang.finalsql.utils.TabCache;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lingkang
 * Created by 2022/4/11
 */
public class SqlGenerate {
    private SqlDialect dialect;
    private SqlConfig sqlConfig;

    public SqlGenerate(SqlDialect dialect, SqlConfig sqlConfig) {
        this.dialect = dialect;
        this.sqlConfig = sqlConfig;
    }

    public <T> ExSqlEntity querySql(Class<T> clazz, Condition condition) {
        ClassUtils.addToCache(clazz);
        // 基础查询
        ExSqlEntity exSqlEntity = columnAndTable(clazz);
        // 添加条件
        addQueryCondition(exSqlEntity, condition);
        exSqlEntity.setSql("select " + exSqlEntity.getSql());
        return exSqlEntity;
    }

    public <T> ExSqlEntity oneSql(Class<T> clazz, Condition condition) {
        ClassUtils.addToCache(clazz);
        // 基础查询
        ExSqlEntity exSqlEntity = columnAndTable(clazz);
        // 添加条件
        addQueryCondition(exSqlEntity, condition);
        // selectOne 方言
        exSqlEntity.setSql(dialect.one(exSqlEntity.getSql()));
        return exSqlEntity;
    }

    public <T> ExSqlEntity countSql(Class<T> clazz, Condition condition) {
        ClassUtils.addToCache(clazz);
        ExSqlEntity exSqlEntity = new ExSqlEntity();
        exSqlEntity.setSql(" from " + NameUtils.getTableName(clazz, dialect));
        addQueryCondition(exSqlEntity, condition);
        exSqlEntity.setSql(dialect.count(exSqlEntity.getSql()));
        return exSqlEntity;
    }

    public ExSqlEntity selectRowSql(ExSqlEntity exSqlEntity, int row) {
        exSqlEntity.setSql(dialect.rowSql(exSqlEntity.getSql(), 0, row));
        return exSqlEntity;
    }

    public <T> ExSqlEntity insertSql(T t) {
        ClassUtils.addToCache(t.getClass());
        return insert(t);
    }

    public <T> ExSqlEntity batchInsert(List<T> entity) {
        String sql = "";
        List<Object> param = new ArrayList<>();
        boolean isFirst = false;
        int start = 0, eq = entity.size() - 1;
        for (int i = 0; i < entity.size(); i++) {
            T t = entity.get(i);
            ExSqlEntity exSqlEntity = this.insertSql(t);
            if (!isFirst) {
                sql += exSqlEntity.getSql() + "";
                start = sql.indexOf("values") + 7;
                isFirst = true;
            } else {
                sql += exSqlEntity.getSql().substring(start);
            }
            if (i == eq) {
                sql += ";";
            } else {
                sql += ",\n";
            }
            param.addAll(exSqlEntity.getParam());
        }
        return new ExSqlEntity(sql, param);
    }

    public <T> ExSqlEntity updateSql(T entity, Condition condition) {
        ClassUtils.addToCache(entity.getClass());
        return update(entity, condition);
    }

    public <T> ExSqlEntity deleteSql(T entity, Condition condition) {
        ClassUtils.addToCache(entity.getClass());
        return delete(entity, condition);
    }

    public <T> ExSqlEntity deleteSql(Class<T> entity, List<Object> ids) {
        return deleteByIds(entity, ids);
    }

    // --------------------  非主要  ----------------------------------------

    //  where 1=1 and aa=bb ...
    private void addQueryCondition(ExSqlEntity exSqlEntity, Condition condition) {
        if (condition != null) {
            ExSqlEntity exSql = condition.getSql();
            if (exSql.getParam() != null) {
                exSqlEntity.setSql(exSqlEntity.getSql() + " where 1=1 " + exSql.getSql());
                exSqlEntity.getParam().addAll(exSql.getParam());
            }
            if (condition.getOrder() != null) {
                exSqlEntity.setSql(exSqlEntity.getSql() + condition.getOrder());
            }
        }
    }

    //  where 1=1 and aa=bb ...
    private <T> void addQueryEntityCondition(T entity, ExSqlEntity exSqlEntity, Condition condition) {
        TabCache tabCache = ClassUtils.tabCache.get(entity.getClass());
        String sql = " where 1=1 ";
        List<Object> param = new ArrayList<>();

        for (int i = 0; i < tabCache.getColumnField().length; i++) {
            Object value = ClassUtils.getValue(entity, tabCache.getColumnField()[i]);
            if (value != null) {
                sql += " and " + tabCache.getColumnName()[i] + "=?";
                param.add(value);
            }
            // 忽略空值
        }
    }

    // colName as col_name ... from table_name
    private <T> ExSqlEntity columnAndTable(Class<T> clazz) {
        TabCache tabCache = ClassUtils.tabCache.get(clazz);
        String col = "";
        for (int i = 0; i < tabCache.getColumnName().length; i++) {
            // 查询列
            col += tabCache.getColumnName()[i] + " as " + tabCache.getFieldName()[i] + ", ";
        }
        col = col.substring(0, col.length() - 2);
        ExSqlEntity exSqlEntity = new ExSqlEntity();
        exSqlEntity.setSql(col + " from " + NameUtils.getTableName(clazz, dialect));
        return exSqlEntity;
    }

    private <T> ExSqlEntity columnAndTableEntity(T entity) {
        TabCache tabCache = ClassUtils.tabCache.get(entity.getClass());
        String col = "";


        for (int i = 0; i < tabCache.getColumnName().length; i++) {
            // 查询列
            col += tabCache.getColumnName()[i] + " as " + tabCache.getFieldName()[i] + ", ";
        }
        col = col.substring(0, col.length() - 2);
        ExSqlEntity exSqlEntity = new ExSqlEntity();
        exSqlEntity.setSql(col + " from " + tabCache.getTableName());
        return exSqlEntity;
    }

    private <T> ExSqlEntity insert(T entity) {
        Class<?> clazz = entity.getClass();
        TabCache tabCache = ClassUtils.tabCache.get(clazz);

        // 检查
        Field[] declaredFields = tabCache.getColumnField();
        if (declaredFields.length < 1) {
            throw new FinalSqlException("插入对象属性不能为空：无 @Id 或 @Column 注解。" + clazz);
        }
        if (tabCache.getIdType() == IdType.INPUT) {
            if (ClassUtils.getValue(entity, tabCache.getIdColumnField()) == null) {
                throw new FinalException("实体对象 @Id 类型为 IdType.INPUT，则主键 id 的值不能为空！");
            }
        }

        // 表
        String sql = "insert into " + NameUtils.getTableName(clazz, dialect);
        ExSqlEntity exSqlEntity = new ExSqlEntity();

        String val = "";
        sql += " (";
        List<Object> param = new ArrayList<>();
        for (int index = 0; index < tabCache.getColumnField().length; index++) {
            Field field = declaredFields[index];
            if (field == tabCache.getIdColumnField()) {// 当前属性值是主键ID
                Id id = field.getAnnotation(Id.class);
                if (id.value() == IdType.AUTO) { // 实体类设置ID是自动生成时
                    if (!"".equals(id.sequence())) {
                        // 否则类似postgresql类协议
                        String nextval = dialect.nextval(id.sequence());

                        sql += tabCache.getColumnName()[index] + ", ";
                        // postgresql类协议
                        val += nextval + ", ";
                        continue;
                    }
                }
            }
            // 主键非自动生成
            Object o = ClassUtils.getValue(entity, field);
            if (o != null) {
                sql += tabCache.getColumnName()[index] + ", ";
                param.add(o);
                val += "?, ";
            }
        }

        if (param.isEmpty()) {
            throw new FinalSqlException("不能插入空对象：" + entity);
        }

        exSqlEntity.setParam(param);
        sql = sql.substring(0, sql.length() - 2) + ")";
        sql += " values (" + val.substring(0, val.length() - 2) + ")";
        exSqlEntity.setSql(sql);
        return exSqlEntity;
    }

    private <T> ExSqlEntity update(T entity, Condition condition) {
        Class<?> clazz = entity.getClass();
        TabCache tabCache = ClassUtils.tabCache.get(clazz);

        boolean hasCondition = false;
        if (condition != null && condition.hasWhere()) {
            hasCondition = true;
        }

        if (!hasCondition) {
            if (tabCache.getIdColumnField() == null) {
                throw new FinalSqlException("更新对象中, 未添加@Id注解确定主键列！" + clazz);
            }
            Object id = ClassUtils.getValue(entity, tabCache.getIdColumnField());
            if (id == null) {
                throw new FinalSqlException("更新对象中主键Id的值为空！");
            }
        }

        // 表
        String sql = "update " + NameUtils.getTableName(clazz, dialect);
        ExSqlEntity exSqlEntity = new ExSqlEntity();

        sql += " set ";
        String set = "";
        List<Object> params = new ArrayList<>();
        for (int i = 0; i < tabCache.getColumnField().length; i++) {
            Object value = ClassUtils.getValue(entity, tabCache.getColumnField()[i]);
            if (value != null) {
                set += ", " + tabCache.getColumnName()[i] + "=?";
                params.add(value);
            }
            // 忽略空值
        }
        if (set.length() > 1) {
            set = set.substring(1);
        }
        sql += set; // 完成 where 前部分

        sql += " where 1=1";


        if (hasCondition) { // 存在条件
            ExSqlEntity exSql = condition.getSql();
            sql += exSql.getSql();
            params.addAll(exSql.getParam());
        } else {
            Object idValue = ClassUtils.getValue(entity, tabCache.getIdColumnField());
            sql += " and " + tabCache.getIdColumnName() + "=?";
            params.add(idValue);
        }

        exSqlEntity.setParam(params);
        exSqlEntity.setSql(sql);
        return exSqlEntity;
    }

    private <T> ExSqlEntity delete(T entity, Condition condition) {
        Class<?> clazz = entity.getClass();
        TabCache tabCache = ClassUtils.tabCache.get(clazz);
        Field[] columnField = tabCache.getColumnField();


        // 表
        String sql = "delete from " + NameUtils.getTableName(clazz, dialect);
        ExSqlEntity exSqlEntity = new ExSqlEntity();

        sql += " where 1=1";
        List<Object> param = new ArrayList<>();
        for (int i = 0; i < columnField.length; i++) {
            Object value = ClassUtils.getValue(entity, columnField[i]);
            if (value != null) {
                sql += " and " + tabCache.getColumnName()[i] + "=?";
                param.add(value);
            }
            // 忽略空值
        }

        boolean hasCondition = false;
        if (condition != null && condition.hasWhere()) {
            hasCondition = true;
        }

        if (param.size() == 0 && !hasCondition) {
            throw new FinalSqlException("不支持使用整表数据删除，请添加参数条件！实体类：" + entity.getClass() + "\n 若想整表数据删除，可添加条件 1=1");
        }

        if (hasCondition) { // 存在条件
            ExSqlEntity exSql = condition.getSql();
            sql += exSql.getSql();
            param.addAll(exSql.getParam());
        }

        exSqlEntity.setParam(param);
        exSqlEntity.setSql(sql);
        return exSqlEntity;
    }

    private <T> ExSqlEntity deleteByIds(Class<T> t, List<Object> ids) {
        // 表
        String sql = "delete from " + NameUtils.getTableName(t, dialect);
        Field idColumn = ClassUtils.getIdColumn(t.getDeclaredFields());
        if (idColumn == null) {
            throw new FinalException("对象中找不到 @Id 注解，无法获取 Id 字段");
        }
        sql += " where " + NameUtils.unHump(idColumn.getName()) + " in (" + Condition.getIn(ids.size()) + ")";
        return new ExSqlEntity(sql, ids);
    }

    public ExSqlEntity humpUpdate(String sql, List params) {
        // update tableName set createTime=? where orderId=1 and uId in (?,?)
        // update table_name set create_time=? where order_id=1 and u_id in (?,?)
        return new ExSqlEntity(NameUtils.unHump(sql), params);
    }
}
